package com.example;

import java.util.HashMap;
import java.util.Map;

// 漏斗限流
// 漏斗的容量是有限的，如果将漏嘴堵住，然后一直往里面灌水，
// 它就会变满，直至再也装不进去。如果将漏嘴放开，水就会往下流，流走一部分之后，
// 就又可以继续往里面灌水。如果漏嘴流水的速率大于灌水的速率，那么漏斗永远都
// 装不满。如果漏嘴流水速率小于灌水的速率，那么一旦漏斗满了，灌水就需要暂停
// 并等待漏斗腾出一部分空间。
public class FunnelRateLimiter {

    private Map<String, Funnel> map = new HashMap<>();

    // 漏斗
    private static class Funnel{

        private int capacity; // 漏斗的容量
        private int leakageRate; // 漏水的速率
        private long lastLeakTime;// 上一次漏水的时间
        private int leftCapacity; // 漏斗的剩余空间

        public Funnel(int capacity, int leakageRate) {
            this.capacity = capacity;
            this.leakageRate = leakageRate;
            this.lastLeakTime = System.currentTimeMillis();
            this.leftCapacity = capacity;
        }

        // 漏水
        public void leaking(){
            // 间隔时间
            long deltaTime = System.currentTimeMillis() - lastLeakTime;
            // 间隔时间内流逝的水量
            int deltaQuota = (int) (leakageRate * deltaTime);
            // 间隔时间太长导致整数溢出了
            if(deltaQuota < 0){
                lastLeakTime = System.currentTimeMillis();
                leftCapacity = capacity;
                return;
            }
            lastLeakTime = System.currentTimeMillis();
            leftCapacity += deltaQuota;
            if(leftCapacity > capacity){
                leftCapacity = capacity;
            }
        }

        // 灌水
        public boolean watering(int quota){
            leaking();
            if(leftCapacity >= quota){
                leftCapacity -= quota;
                return true;
            }
            return false;
        }
    }

    public boolean isActionAllowed(String userId, String actionId, int capacity, int leakingRate){
        String key = String.format("%s:%s", userId, actionId);
        Funnel funnel = map.get(key);
        if(funnel == null){
            funnel = new Funnel(capacity, leakingRate);
            map.put(key, funnel);
        }
        return funnel.watering(1);
    }


}
